﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include "GnufBase.h"
#include "GnufUiCommonDef.h"

class QWidget;
class QObject;

namespace gnuf {
    class Item;

    class QtItem : public gcmp::WeakReferenceable
    {
    public:
        static QtItem* makeQtItem(Item* item, QWidget* parent = nullptr, Direction dir = Direction::Horizontal);
    public:
        virtual ~QtItem();
        virtual void destroy() = 0;
        virtual void disconnectFromItem() = 0;
        virtual QWidget * castQWidget() = 0;
        virtual const QWidget * castQWidget() const = 0;
        enum class EWhatDirty : std::uint16_t
        {
            eInfo,
            eVisibility,
            eAbility,
            eSelection,
            eAppearanceSetting,
            eBlockVisibility,
            eReadOnly,
            eBadgeVisibility,
            eSprite
        };
        virtual void refresh(EWhatDirty) = 0;

    protected:
        explicit QtItem(const char *);
        void destroyImmediately();
        void setQtObjectName(const Item *, QObject *) const;
        void clearQtObjectNamesUnder(const Item *, bool);

    private:
        QtItem(QtItem&) = delete;
        QtItem& operator=(const QtItem&) = delete;

    private:
        const gcmp::UniqueString * m_className;
    };

    template <class TQtClass, class TItem, class... QtArgs>
    class QtItemWith : public TQtClass, public QtItem
    {
    protected:
        explicit QtItemWith(const char * constructor, TItem * item, QtArgs... qtArgs)
            : TQtClass(std::forward<QtArgs>(qtArgs)...)
            , QtItem(constructor)
            , m_item(item)
        {
            //
            // Make sure that the item did not connect with any QtItem.
            DBG_WARN_UNLESS(nullptr != item && !item->hasQtItem(),
                L"The Item should not have connected with any Qt item!", L"GMEP", L"2020-08-18");

            //
            // Every Qt object has an object name for supporting Joural logic.
            // So, the class inheriting from QtItemWith template class should 
            // inherit from QObject class, at least.
            if (QObject * qtMe = qobject_cast<QObject *>(this)) {
                setQtObjectName(item, qtMe);
            }
            else {
                DBG_WARN(L"A QtItemWith should inherit from QObject, at least!", L"GMEP", L"2020-08-18");
            }
        }

        virtual ~QtItemWith()
        {
            disconnectFromItem();
        }

    public:
        // Return the item.
        inline TItem * item() const { return m_item; }

        // Set the object name for those children of this QtItem.
        void setChildQtObjectName(QWidget * qtChild, int index = -1) const
        {
            if (const QObject * qtMe = qobject_cast<const QObject *>(this)) {
                DBG_WARN_AND_RETURN_VOID_UNLESS(qtChild != nullptr, L"qtChild is nullptr", L"jianggy-a", L"2021-06-22");
                const QMetaObject * qtMeta = qtChild->metaObject();
                DBG_WARN_AND_RETURN_VOID_UNLESS(nullptr != qtMeta,
                    L"Null qtMeta!", L"GMEP", L"2022-08-02");
                qtChild->setObjectName(
                    qtMe->objectName() + QString("->") +
                    qtMeta->className() +
                    (index > -1 ? QString("#%1").arg(index) : QString()));
            }
            else {
                DBG_WARN(L"Failed to set the object name of the child widget!", L"GMEP", L"2020-08-19");
            }
        }

        // Disconnect from the item.
        virtual void disconnectFromItem() override
        {
            if (nullptr != m_item)
            {
                m_item->setQtItem(nullptr);
                m_item = nullptr;
            }
        }

        // Cast to the QWidget.
        virtual QWidget * castQWidget() override
        {
            if (QWidget * qtWidget = qobject_cast<QWidget *>(this)) {
                return qtWidget;
            }
            return nullptr;
        }

        // Cast to the QWidget.
        virtual const QWidget * castQWidget() const override
        {
            if (const QWidget * qtWidget = qobject_cast<const QWidget *>(this)) {
                return qtWidget;
            }
            return nullptr;
        }
        
        // Destroy.
        virtual void destroy() override
        {
            if (destroyAsQtWidget()) {
                return;
            }
            
            destroyImmediately();

            //////////////////////////////////////////////////////////
            // Note: Do not want to add any logic below, since this //
            // object has been deleted, or deleted later at least.  //
            //////////////////////////////////////////////////////////
        }

        void clearQtObjectNames()
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(nullptr != m_item,
                L"Cannot call this method in this time!", L"GMEP", L"2020-08-21");
            clearQtObjectNamesUnder(m_item, true);
        }

        // 查询数据模型里的可见性
        // bug fix[shaoth]: AD-12030 【单机版】通用工具处，排布没对齐
        bool dataModelVisible() const
        {
            return m_item->visible();
        }
        
    protected:
        bool destroyAsQtWidget()
        {
            QWidget * qtWidget = castQWidget();
            if (nullptr == qtWidget) {
                return false;
            }

            //
            // Disconnect from the item.
            disconnectFromItem();
            
            //
            // Prepare something before deleting, in order to support the 
            // Journal logic that does not take care of those hidden widgets.
            qtWidget->setObjectName(QString());
            qtWidget->setVisible(false);

            //
            // Disconnect from the Qt instance tree, and then delete later.
            qtWidget->setParent(nullptr);
            qtWidget->deleteLater();

            return true;
        }

        void clearQtObjectNamesUnderMe()
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(nullptr != m_item,
                L"Cannot call this method in this time!", L"GMEP", L"2020-08-21");
            clearQtObjectNamesUnder(m_item, false);
        }

    private:
        TItem * m_item;
    };
}
